﻿using CXW.Core.Sda;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace CXW.Core.Sharp.Sda
{
    public partial class ExceptionBox : BaseForm
    {
        Exception exceptionThrown;
        string message;

        public ExceptionBox()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Creates a new ExceptionBox instance.
        /// </summary>
        /// <param name="exception">The exception to display</param>
        /// <param name="message">An additional message to display</param>
        /// <param name="mustTerminate">If <paramref name="mustTerminate"/> is true, the
        /// continue button is not available.</param>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public ExceptionBox(Exception exception, string message, bool mustTerminate) : this()
        {
            this.exceptionThrown = exception;
            this.message = message;

            if (mustTerminate)
            {
                closeButton.Visible = false;
                continueButton.AccessibleDescription = closeButton.AccessibleDescription;
                continueButton.Left -= closeButton.Width - continueButton.Width;
                continueButton.Width = closeButton.Width;
            }

            try
            {
                Translate(this);
            }
            catch { }

            exceptionTextBox.Text = getClipboardString();
        }

        internal static void RegisterExceptionBoxForUnhandledExceptions()
        {
            Application.ThreadException += (sender, e) =>
            {
                LoggingService.Error("ThreadException caught", e.Exception);
                ShowErrorBox(e.Exception, null);
            };
            AppDomain.CurrentDomain.UnhandledException += ShowErrorBox;
            System.Windows.Threading.Dispatcher.CurrentDispatcher.UnhandledException += (sender, e) =>
            {
                ShowErrorBox(e.Exception, "Unhandled WPF exception", false);
                e.Handled = true;
            };
        }


        [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")]
        static void ShowErrorBox(object sender, UnhandledExceptionEventArgs e)
        {
            Exception ex = e.ExceptionObject as Exception;
            LoggingService.Fatal("UnhandledException caught", ex);
            if (e.IsTerminating)
                LoggingService.Fatal("Runtime is terminating because of unhandled exception.");
            ShowErrorBox(ex, "Unhandled exception", e.IsTerminating);
        }

        /// <summary>
        /// Displays the exception box.
        /// </summary>
        public static void ShowErrorBox(Exception exception, string message)
        {
            ShowErrorBox(exception, message, false);
        }

        [ThreadStatic]
        static bool showingBox;

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        static void ShowErrorBox(Exception exception, string message, bool mustTerminate)
        {
            if (exception == null)
                throw new ArgumentNullException("exception");

            // ignore reentrant calls (e.g. when there's an exception in OnRender)
            if (showingBox)
                return;
            showingBox = true;
            try
            {
                using (ExceptionBox box = new ExceptionBox(exception, message, mustTerminate))
                {
                    if (WorkbenchSingleton.InvokeRequired)
                        box.ShowDialog();
                    else
                        box.ShowDialog(WorkbenchSingleton.Workbench);
                }
            }
            catch (Exception ex)
            {
                LoggingService.Warn("Error showing ExceptionBox", ex);
                MessageBox.Show(exception.ToString(), message, MessageBoxButtons.OK,
                                MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
            }
            finally
            {
                showingBox = false;
            }
        }

        void Translate(Control ctl)
        {
            ctl.Text = ResourceService.GetString(ctl.AccessibleDescription);
            foreach (Control child in ctl.Controls)
            {
                Translate(child);
            }
        }

        string getClipboardString()
        {
            StringBuilder sb = new StringBuilder();

            GetVersionInformationString(sb);
            sb.AppendLine();

            if (message != null)
            {
                sb.AppendLine(message);
            }
            sb.AppendLine("Exception thrown:");
            sb.AppendLine(exceptionThrown.ToString());
            sb.AppendLine();
            sb.AppendLine("---- Recent log messages:");
            try
            {
                RecentLogMessages.AppendRecentLogMessages(sb);
            }
            catch (Exception ex)
            {
                sb.AppendLine("Failed to append recent log messages.");
                sb.AppendLine(ex.ToString());
            }
            sb.AppendLine();

            return sb.ToString();
        }

        void GetVersionInformationString(StringBuilder sb)
        {
            object[] attr = typeof(ExceptionBox).Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
            if (attr.Length == 1)
            {
                AssemblyInformationalVersionAttribute aiva = (AssemblyInformationalVersionAttribute)attr[0];
                sb.AppendLine("Version            : " + aiva.InformationalVersion);
            }
            sb.AppendLine(".Net Version         : " + Environment.Version);
            sb.AppendLine("OS Version           : " + Environment.OSVersion);
            string cultureName = null;
            try
            {
                cultureName = CultureInfo.CurrentCulture.Name;
                sb.AppendLine("Current Culture      : " + CultureInfo.CurrentCulture.EnglishName + "(" + cultureName + ")");
            }
            catch { }
            try
            {
                if (cultureName == null || !cultureName.StartsWith(ResourceService.Language))
                { sb.AppendLine("Current UI Culture   : " + ResourceService.Language); }
            }
            catch { }
            try
            {
                if (IntPtr.Size != 4) { sb.AppendLine("Running as " + (IntPtr.Size * 8) + " bit process"); }
                string PROCESSOR_ARCHITEW6432 = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432");
                if (!string.IsNullOrEmpty(PROCESSOR_ARCHITEW6432))
                {
                    if (PROCESSOR_ARCHITEW6432 == "AMD64")
                        PROCESSOR_ARCHITEW6432 = "x86-64";
                    sb.AppendLine("Running under WOW6432, processor architecture: " + PROCESSOR_ARCHITEW6432);
                }
            }
            catch { }
            try
            {
                if (SystemInformation.TerminalServerSession)
                {
                    sb.AppendLine("Terminal Server Session");
                }
                if (SystemInformation.BootMode != BootMode.Normal)
                {
                    sb.AppendLine("Boot Mode            : " + SystemInformation.BootMode);
                }
            }
            catch { }
            sb.AppendLine("Working Set Memory   : " + (Environment.WorkingSet / 1024) + "kb");
            sb.AppendLine("GC Heap Memory       : " + (GC.GetTotalMemory(false) / 1024) + "kb");
        }

        void CopyInfoToClipboard()
        {
            if (copyErrorCheckBox.Checked)
            {
                string exceptionText = exceptionTextBox.Text;
                if (Application.OleRequired() == ApartmentState.STA)
                {
                    ClipboardWrapper.SetText(exceptionText);
                }
                else
                {
                    Thread th = new Thread((ThreadStart)delegate
                    {
                        ClipboardWrapper.SetText(exceptionText);
                    });
                    th.Name = "CopyInfoToClipboard";
                    th.SetApartmentState(ApartmentState.STA);
                    th.Start();
                }
            }
        }


        void reportButton_Click(object sender, EventArgs e)
        {
            CopyInfoToClipboard();

        }

        void continueButtonClick(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Ignore;
            Close();
        }

        void CloseButtonClick(object sender, EventArgs e)
        {
            if (MessageService.AskQuestion("ExceptionBox.QuitWarning"))
            {
                Process.GetCurrentProcess().Kill();
            }
        }
    }
}
